<?php

declare(strict_types=1);

namespace app\api\service;

use think\facade\Db;
use think\facade\Log;

class Discount
{

    protected $params; // 参数
    protected $discountInfo; // 折扣信息
    protected $originalTotalPrice; // 原总价
    protected $totalNumber; // 总数量
    protected $productList; // 产品列表
    // 定义折扣类型常量
    const DISCOUNT_CATEGORY_PRODUCT = 1;
    const DISCOUNT_CATEGORY_ORDER = 2;

    const DISCOUNT_TYPE_PERCENTAGE = 1;
    const DISCOUNT_TYPE_FIXED = 2;
    /*
       优惠券码A是否能使用及折扣后产品总价判断
     1、当前用户是否是经销商用户，是则不能使用
     2、如果当前用户是从GoAffPro的推广链接过来的(接口的头部有X-GOAFFPRO-AFFINFO参数并有值)，
     先从db_aff_coupons中查询是否有有效的A的记录，没有则再从db_discount中查询是否有有效的A的记录，没有则不能使用；
     如果用户不是从GoAffPro的推广链接过来的(接口的头部没有X-GOAFFPRO-AFFINFO参数)，则从db_discount中查询是否有有效的A的记录，没有则不能使用。
     3、A在db_aff_coupons表中的，则产品总价重新计算为按原价计算的产品总价*(100 - discount_value) / 100;
     A在db_discount表中的，则判断当前用户是否满足使用资格(使用者限制)、最大使用次数限制、最低购买要求，
     都满足，则：
     如果A的折扣类别是Amount off products且折扣类型是percentage，在限定的使用产品原价*(100 - discount_value) / 100 * 购买数量，非限定的使用产品原价 * 购买数量 计算出新的商品总价；
     如果A的折扣类别是Amount off products且折扣类型是fixed amount且用户购买的任意商品在适用产品列表中，则按照原价计算的商品总价 - discount_value 计算出新的商品总价；
     如果A的折扣类别是Amount off orders且折扣类型是percentage，则按照原价计算的商品总价*(100 - discount_value) / 100 计算出新的商品总价；
     如果A的折扣类别是Amount off orders且折扣类型是fixed amount，则按照原价计算的商品总价 - discount_value 计算出新的商品总价；
    */


    /**
     * @describe
     * @author: Bobo
     * @date: 2025-05-14 19:00:54 
     * @param [type] @required
     * @return Renderable
     */
    public function writCouponUsageRecord($order_id, $customer_id, $aff_order_id, $affiliate_id, $discount_id, $discount_code)
    {
        $data = [
            'order_id' => $order_id,
            'customer_id' => $customer_id,
            'aff_order_id' => $aff_order_id,
            'affiliate_id' => $affiliate_id,
            'discount_id' => $discount_id,
            'discount_code' => $discount_code,
            'create_time' => date('Y-m-d H:i:s'),
        ];
        Db::name('order_relation')->insert($data);
    }

    /**
     * @describe
     * @author: Bobo
     * @date: 2025-05-14 13:47:38 
     * @param [type] @required
     * @return Renderable
     */
    public function checkCode($params)
    {
        try {
            // 基础验证
            $validateResult = $this->validateCheckData($params);
            if ($validateResult['code'] !== 0) {
                return $validateResult;
            }

            $this->params = $params;
            $this->productList = $params['product_list'];
            // 排序，保证价格一致
            usort($this->productList, function ($a, $b) {
                return $a['id'] - $b['id'];
            });

            // 计算初始值
            $this->calculateInitialValues();



            // 检查是否是GoAffPro推广
            if ($this->isGoAffProPromotion()) {
                $goAffProDiscount = $this->handleGoAffProDiscount();
                if ($goAffProDiscount) {
                    return $goAffProDiscount;
                }
            }

            // 获取并验证折扣信息
            $this->discountInfo = $this->getDiscountInfo();
            if (!$this->discountInfo) {
                return ['code' => 1, 'message' => 'Invalid code'];
            }

            // 验证折扣有效性
            $validationResult = $this->validateDiscount();
            if ($validationResult['code'] !== 0) {
                return $validationResult;
            }
            // 处理产品适用性
            $applicabilityResult = $this->handleProductApplicability();
            if ($applicabilityResult['code'] !== 0) {
                return $applicabilityResult;
            }
            // 计算折扣金额
            return $this->calculateDiscountAmount();
        } catch (\Exception $e) {
            // 记录错误日志
            Log::error(
                '折扣码验证异常: ' . $e->getMessage() .
                    ' | 文件：' . $e->getFile() .
                    ' | 行号：' . $e->getLine() .
                    'params' . json_encode($this->params, JSON_UNESCAPED_UNICODE)
            );

            return [
                'code' => 1,
                'message' => '折扣码验证失败: ' . $e->getMessage()
            ];
        }
    }
    /**
     * @describe  我的优惠券
     * @author: Bobo
     * @date: 2025-05-14 13:47:38
     * @param [type] @required
     * @return Renderable
     */
    public function myCoupon($params)
    {
        $this->params = $params;
        $return = [];
        $sql = $list = Db::name('discount')->where(['is_del' => 0, 'source' => 1, 'status' => 1])
            ->whereRaw(' eligibility_type = ? or ( eligibility_type = ? and find_in_set( ? ,eligibility_value) ) ', [1, 3, $params['user_id']]);
        $total = $sql->count();
        if (!$total) {
            return [
                'code' => 0,
                'message' => 'success',
                'data' => ['list' => [], 'total' => 0]
            ];
        }
        $list = $sql->select()->toArray();
        $discount_ids = array_column($list, 'id');
        #  查询优惠券使用次数
        $totalUses = Db::name('order_relation')
            ->where([
                ['discount_id', 'in', $discount_ids],
                ['is_del', '=', 0],
            ])
            ->field('discount_id ,count(id) as total ')->group('discount_id')->select()->toArray();
        $totalUses = array_column($totalUses, null, 'discount_id');
        #  查询优惠券用户使用次数
        $userTotalUse = Db::name('order_relation')
            ->where([
                ['discount_id', 'in', $discount_ids],
                ['is_del', '=', 0],
                ['customer_id', '=', $params['user_id']],
            ])
            ->field('discount_id ,count(id) as total ')->group('discount_id')->select()->toArray();
        $userTotalUse = array_column($userTotalUse, null, 'discount_id');
        if ($list) {
            foreach ($list as $k => $value) {
                $return[$k]['code_title'] = $value['code_title'];
                $return[$k]['discount_type'] = $value['discount_type'];
                $return[$k]['discount_value'] = $value['discount_value'];
                $return[$k]['applies_to'] = 'For Online Store';
                $return[$k]['activedate_start'] = $value['activedate_start'];
                $return[$k]['activedate_end'] = $value['activedate_end'];
                $return[$k]['status'] = $value['status'];
                $str = $value['discount_type'] == 1 ? $value['discount_value'] . '%' : '$' . $value['discount_value'];
                $type = $value['discount_category'] == 1 ? " off products" : " off entire order";
                $return[$k]['discount_category'] = $str . $type;
                $return[$k]['limit'] = $this->getLimit($value);
                $this->discountInfo =   $value;
                // 状态双重校验
                if (!$this->isDiscountTimeValid() || !$this->bulkUsageLimitValid($value, $totalUses, $userTotalUse)) {
                    $return[$k]['status'] = 0;
                }
                #根据状态筛选
                if (isset($params['status']) && $return[$k]['status'] != $params['status']) {
                    unset($return[$k]);
                }
            }
        }
        #排序
        usort($return, function ($a, $b) {
            #status 降序
            if ($a['status'] !== $b['status']) {
                return $b['status'] <=> $a['status'];
            }
            // activedate_start 升序
            $dateA = strtotime($a['activedate_start']);
            $dateB = strtotime($b['activedate_start']);
            return $dateA <=> $dateB;
        });
        #处理分页
        $offset = ($params['page'] - 1) * $params['listRows'];
        $currentData = array_slice($return, (int)$offset, (int)$params['listRows']);
        return [
            'code' => 0,
            'message' => 'success',
            'data' => ['list' => $currentData, 'total' => $total]
        ];
    }

    public function getLimit($value): string
    {
        $use = '';
        switch ($value['min_purchase_req_type']) {
            case 1:
                $min = 'No minimum requirements';
                break;
            case 2:
                $min = "Minimum purchase amount {$value['min_purchase_req_value']}";
                break;
            default:
                $min = "Minimum quantity of " . $value['min_purchase_req_value'];
        }
        if ($value['max_discount_uses_type']) {

            switch ($value['max_discount_uses_type']) {
                case 1:
                    $use = ",Limit of {$value['max_discount_uses_value']} uses";
                    break;

                case 2:
                    $use = ",One use per customer";
                    break;

                default:
                    $use =  ",Limit of {$value['max_discount_uses_value']} uses,One use per customer";
                    break;
            }
        }
        return $min . $use;
    }

    /**
     * @describe
     * @author: Bobo
     * @date: 2025-05-14 13:52:35 
     * @param [type] @required
     * @return Renderable
     */
    protected function validateCheckData($params)
    {
        if (!isset($params['user_id']) || empty($params['user_id'])) {
            return ['code' => 1, 'message' => 'Invalid user_id'];
        }
        if (!isset($params['role']) || !in_array($params['role'], [1])) {
            return ['code' => 1, 'message' => 'Invalid role'];
        }

        if (!isset($params['code']) || empty($params['code'])) {
            return ['code' => 1, 'message' => 'Invalid code'];
        }

        if (!isset($params['product_list']) || empty($params['product_list'])) {
            return ['code' => 1, 'message' => 'Invalid product_list'];
        }

        foreach ($params['product_list'] as $product) {
            if (!isset($product['id']) || $product['id'] <= 0) {
                return ['code' => 1, 'message' => 'Invalid product_id'];
                break;
            }

            if (!isset($product['number']) || $product['number'] <= 0) {
                return ['code' => 1, 'message' => 'Invalid quantity'];
                break;
            }

            if (!isset($product['price']) || $product['price'] <= 0) {
                return ['code' => 1, 'message' => 'Invalid price'];
                break;
            }
        }

        return ['code' => 0];
    }

    /**
     * 计算初始值
     */
    protected function calculateInitialValues()
    {
        $this->originalTotalPrice = array_sum(array_map(function ($product) {
            return $product['price'] * $product['number'];
        }, $this->productList));

        $this->totalNumber = array_sum(array_column($this->productList, 'number'));
    }

    /**
     * 检查是否是GoAffPro推广
     */
    protected function isGoAffProPromotion()
    {
        return isset($_SERVER['HTTP_X_GOAFFPRO_AFFINFO']);
    }

    /**
     * 处理GoAffPro折扣
     */
    protected function handleGoAffProDiscount()
    {
        // GoAffPro折扣处理逻辑
        $affiliateInfo = json_decode($_SERVER['HTTP_X_GOAFFPRO_AFFINFO'], true);

        if (!empty($affiliateInfo) && isset($affiliateInfo['id']) && isset($affiliateInfo['ref_code'])) {
            $aff_coupons_res = Db::name('aff_coupons')
                ->where([
                    'is_del' => 0,
                    'affiliate_id' => $affiliateInfo['id'],
                    'coupon_code' => $this->params['code'],
                    'discount_type' => 'percentage'
                ])
                ->field('id,discount_value')
                ->find();
            if ($aff_coupons_res) {


                $this->productList = array_map(function ($product) use ($aff_coupons_res) {
                    $product['price'] = round($product['price'] * (1 - $aff_coupons_res['discount_value'] / 100), 2);
                    return $product;
                }, $this->productList);
                $total_price = array_sum(array_map(function ($product) {
                    return $product['price'] * $product['number'];
                }, $this->productList));

                $discount_amount = round($this->originalTotalPrice - $total_price, 2);
                $discount_id = Db::name('discount')->where(['is_del' => 0, 'source' => 2])->value('id');

                return ['code' => 0, 'message' => 'success', 'data' => [
                    'product_list' => $this->productList,
                    'total_discount' => $discount_amount,
                    'is_effective' => true,
                    'discount_id' => $discount_id,
                ]];
            }
            return false;
        }
        return false;
    }

    /**
     * 获取折扣信息
     */
    protected function getDiscountInfo()
    {
        try {
            return Db::name('discount')
                ->where([
                    'code_title' => $this->params['code'],
                    'status' => 1,
                    'source' => 1,
                    'is_del' => 0,
                ])
                ->find();
        } catch (\Exception $e) {
            Log::error(
                '获取折扣信息异常: ' . $e->getMessage() .
                    ' | 文件：' . $e->getFile() .
                    ' | 行号：' . $e->getLine() .
                    'params' . json_encode($this->params, JSON_UNESCAPED_UNICODE)
            );
            return null;
        }
    }

    /**
     * 验证折扣有效性
     */
    protected function validateDiscount()
    {
        // 验证时间有效性
        if (!$this->isDiscountTimeValid()) {
            return ['code' => 1, 'message' => 'The discount code is not effective'];
        }

        // 验证最低购买要求
        if (!$this->isMinPurchaseRequirementMet()) {
            return ['code' => 1, 'message' => 'The minimum purchase requirement is not met'];
        }

        // 验证用户资格
        if (!$this->isUserEligible()) {
            return ['code' => 1, 'message' => 'The user is not eligible for this discount'];
        }

        // 验证使用次数
        if (!$this->isUsageLimitValid()) {
            return ['code' => 1, 'message' => 'The discount code has been used up'];
        }

        return ['code' => 0];
    }

    /**
     * 验证折扣时间有效性
     */
    protected function isDiscountTimeValid(): bool
    {
        $now = date('Y-m-d H:i:s');
        // 开始日期必须小于等于当前时间
        $isAfterStart = $this->discountInfo['activedate_start'] <= $now;

        // 如果结束日期为空，视为永久有效
        // 否则检查是否在结束日期之前
        $isBeforeEnd = is_null($this->discountInfo['activedate_end']) ||
            $this->discountInfo['activedate_end'] >= $now;

        return $isAfterStart && $isBeforeEnd;
    }

    /**
     * 验证最低购买要求
     */
    protected function isMinPurchaseRequirementMet()
    {
        if ($this->discountInfo['min_purchase_req_type'] == 2) {
            return $this->originalTotalPrice >= $this->discountInfo['min_purchase_req_value'];
        } elseif ($this->discountInfo['min_purchase_req_type'] == 3) {
            return $this->totalNumber >= $this->discountInfo['min_purchase_req_value'];
        }
        return true;
    }

    /**
     * 验证用户资格
     */
    protected function isUserEligible()
    {
        if ($this->discountInfo['eligibility_type'] == 3) {
            $eligibleUsers = explode(',', $this->discountInfo['eligibility_value']);
            return in_array($this->params['user_id'], $eligibleUsers);
        }
        return true;
    }

    /**
     * 验证使用次数限制
     */
    protected function isUsageLimitValid()
    {
        switch ($this->discountInfo['max_discount_uses_type']) {
            case '1':
                return $this->checkTotalUsageLimit();
            case '2':
                return $this->checkPerUserLimit();
            case '1,2':
            case '2,1':
                return $this->checkPerUserLimit() && $this->checkTotalUsageLimit();
            default:
                return true;
        }
    }
    /**
     * 验证使用次数限制
     */
    protected function bulkUsageLimitValid($value, $list, $userList)
    {
        $return = true;
        switch ($value['max_discount_uses_type']) {
            case '1':
                if (isset($list[$value['id']]['total']) && $value['max_discount_uses_value'] <= $list[$value['id']]['total']) {
                    $return = false;
                }
                return $return;
            case '2':
                if (isset($userList[$value['id']]['total']) && 1 <= $userList[$value['id']]['total']) {
                    $return = false;
                }
                return $return;
            case '1,2':
            case '2,1':
                if ((isset($list[$value['id']]['total']) && $value['max_discount_uses_value'] <= $list[$value['id']]['total'])
                    || (isset($userList[$value['id']]['total']) && 1 <= $userList[$value['id']]['total'])
                ) {
                    $return = false;
                }
                return $return;
            default:
                return true;
        }
    }

    /**
     * 检查总使用次数限制
     */
    protected function checkTotalUsageLimit()
    {
        try {
            $totalUses = Db::name('order_relation')
                ->where([
                    'discount_id' => $this->discountInfo['id'],
                    'is_del' => 0,
                ])
                ->count();
            return $totalUses < $this->discountInfo['max_discount_uses_value'];
        } catch (\Exception $e) {
            Log::error(
                '检查总使用次数限制异常: ' . $e->getMessage() .
                    ' | 文件：' . $e->getFile() .
                    ' | 行号：' . $e->getLine() .
                    'params' . json_encode($this->params, JSON_UNESCAPED_UNICODE)
            );
            return false;
        }
    }

    /**
     * 检查每用户使用次数限制
     */
    protected function checkPerUserLimit()
    {
        try {
            $userUses = Db::name('order_relation')
                ->where([
                    'discount_id' => $this->discountInfo['id'],
                    'is_del' => 0,
                    'customer_id' => $this->params['user_id'],
                ])
                ->count();
            return !$userUses;
        } catch (\Exception $e) {
            Log::error(
                '检查每用户使用次数限制异常: ' . $e->getMessage() .
                    ' | 文件：' . $e->getFile() .
                    ' | 行号：' . $e->getLine() .
                    'params' . json_encode($this->params, JSON_UNESCAPED_UNICODE)
            );
            return false;
        }
    }

    /**
     * 处理产品适用性
     */
    protected function handleProductApplicability()
    {
        if ($this->discountInfo['discount_category'] != 1) {
            return ['code' => 0];
        }

        switch ($this->discountInfo['applies_to_type']) {
            case 1:
                $appliesTo = explode(',', $this->discountInfo['applies_to_value']);
                return $this->handleAggregateProducts($appliesTo);
            case 2:
                $appliesTo = explode(',', $this->discountInfo['applies_to_value']);
                return $this->handleSkuProducts($appliesTo);
            default:
                return ['code' => 1, 'message' => 'Invalid product_list'];
        }
    }

    /**
     * 处理聚合产品
     */
    protected function handleAggregateProducts($appliesTo)
    {
        $productIds = array_column($this->productList, 'id');
        $relations = Db::name('product_relation')
            ->whereIn('pid', $productIds)
            ->where('is_del', 0)
            ->field('pid,pm_id')
            ->select()
            ->toArray();

        if (empty($relations)) {
            return ['code' => 1, 'message' => 'Invalid product_list'];
        }

        $applicableProducts = [];
        foreach ($relations as $relation) {
            if (in_array($relation['pm_id'], $appliesTo)) {
                $applicableProducts[$relation['pid']] = true;
            }
        }

        if (empty($applicableProducts)) {
            return ['code' => 1, 'message' => 'The product_list is not applicable to the discount code'];
        }

        foreach ($this->productList as &$product) {
            $product['is_applicable'] = isset($applicableProducts[$product['id']]);
        }

        return ['code' => 0];
    }

    /**
     * 处理SKU产品
     */
    protected function handleSkuProducts($appliesTo)
    {
        $applicableProducts = array_intersect(
            array_column($this->productList, 'id'),
            $appliesTo
        );

        if (empty($applicableProducts)) {
            return ['code' => 1, 'message' => 'The product_list is not applicable to the discount code'];
        }

        foreach ($this->productList as &$product) {
            $product['is_applicable'] = in_array($product['id'], $applicableProducts);
        }

        return ['code' => 0];
    }





    /**
     * 计算折扣金额
     */
    protected function calculateDiscountAmount()
    {
        $discountAmount = 0;
        if ($this->discountInfo['discount_category'] == self::DISCOUNT_CATEGORY_PRODUCT) {
            $discountAmount = $this->calculateProductDiscount();
        } else {
            $discountAmount = $this->calculateOrderDiscount();
        }
        // 清理产品列表中的临时标记
        array_walk($this->productList, function (&$product) {
            unset($product['is_applicable']);
        });

        return [
            'code' => 0,
            'message' => 'success',
            'data' => [
                'product_list' => $this->productList,
                'total_discount' => $discountAmount,
                'is_effective' => $discountAmount,
                'discount_id' => $this->discountInfo['id'],
            ]
        ];
    }

    /**
     * 计算产品折扣
     */
    private function calculateProductDiscount()
    {
        // 百分比折扣
        if ($this->discountInfo['discount_type'] == self::DISCOUNT_TYPE_PERCENTAGE) {
            $discountRate = 1 - ($this->discountInfo['discount_value'] / 100);
            foreach ($this->productList as &$product) {
                if ($product['is_applicable']) {
                    $product['price'] = round($product['price'] * $discountRate, 2);
                }
            }

            $totalPrice = array_sum(array_map(function ($product) {
                return $product['price'] * $product['number'];
            }, $this->productList));

            return round($this->originalTotalPrice - $totalPrice, 2);
        }

        $is_applicable = array_column($this->productList, 'is_applicable');
        $is_applicable = in_array(true, $is_applicable);
        if ($is_applicable) {
            $totalDiscount = round((float)$this->discountInfo['discount_value'], 2);
            $this->shareAlikeProductListDiscount($totalDiscount);
            return $totalDiscount;
        }
        return 0;
    }

    /**
     * 计算订单折扣
     */
    private function calculateOrderDiscount()
    {
        // 百分比折扣
        if ($this->discountInfo['discount_type'] == self::DISCOUNT_TYPE_PERCENTAGE) {
            array_walk($this->productList, function (&$item) {
                $item['price'] = $item['price'] -  $item['price']  * ($this->discountInfo['discount_value'] / 100);
            }); // 使用引用，直接修改原数组
            return round($this->originalTotalPrice * ($this->discountInfo['discount_value'] / 100), 2);
        }

        // 固定金额折扣
        $totalDiscount = round((float)$this->discountInfo['discount_value'], 2);
        if ($totalDiscount <= 0)  return 0;

        $this->shareAlikeProductListDiscount($totalDiscount); // 将总优惠均摊到每个商品上

        return $totalDiscount; // 固定金额折扣直接返回设定的总折扣值
    }

    /**
     * @describe 将总优惠均摊到每个商品上
     * @author: Bobo
     * @date: 2025-05-21 14:55:42 
     * @param [type] @required
     * @return Renderable
     */
    private function shareAlikeProductListDiscount($totalDiscount)
    {
        $totalDiscountAppliedSoFar = 0; // 累加已分配的折扣
        $productCount = count($this->productList);

        // 2. 均摊折扣到前 n-1 组商品
        for ($i = 0; $i < $productCount - 1; $i++) {
            $item = &$this->productList[$i]; // 使用引用，直接修改原数组

            // 计算该商品组应分摊的总折扣金额
            // 规则: totalDiscount * (每组商品的price * number) / totalOrderAmount
            $groupDiscount = $totalDiscount * ($item['price'] * $item['number']) / $this->originalTotalPrice;

            // 计算单个商品的折扣金额 (floor)
            $singleItemDiscount = floor(($groupDiscount / $item['number']) * 100) / 100;

            // 更新商品价格
            $item['price'] = max(0, $item['price'] - $singleItemDiscount);

            // 累加已分配的折扣
            $totalDiscountAppliedSoFar += $singleItemDiscount * $item['number'];
        }

        // 3. 将剩余折扣应用到最后一组商品
        $lastItemIndex = $productCount - 1;
        $lastItem = &$this->productList[$lastItemIndex]; // 使用引用

        // 计算剩余应均摊到最后一组的总折扣
        $remainingDiscount = $totalDiscount - $totalDiscountAppliedSoFar;

        // 计算最后一组单个商品的折扣金额 (确保总折扣准确)
        if ($lastItem['number'] > 0) {
            $singleItemDiscountLast = round($remainingDiscount / $lastItem['number'], 2);
        } else {
            $singleItemDiscountLast = 0;
        }
        // 更新最后一组商品价格
        $lastItem['price'] = $lastItem['price'] - $singleItemDiscountLast;
    }
}
